/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.commons.exception.handler;

import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.exception.api.ExceptionHandler;
import io.iec.edp.caf.commons.exception.entity.CafExceptionHandleException;
import io.iec.edp.caf.commons.exception.entity.ExceptionContext;
import io.iec.edp.caf.commons.exception.entity.ExceptionErrorCode;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import io.iec.edp.caf.commons.exception.logger.ExceptionInnerLogger;
import lombok.extern.slf4j.Slf4j;
import lombok.var;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author Leon Huo
 * @Date: 2021/4/25
 */
@Slf4j
public class DefaultExceptionHandler implements ExceptionHandler {

    /**
     * 异常处理
     *
     * @param exceptionContext 待处理的异常上下文
     */
    @Override
    public Exception handleException(ExceptionContext exceptionContext) {
        if (exceptionContext == null || exceptionContext.getException() == null)
            return null;
        //记录日志
        try {
            Exception exception = exceptionContext.getException();
            ExceptionLogger.writeLog(exception.getMessage(), exception);
        } catch (Exception e) {
            ExceptionInnerLogger.writeLog(exceptionContext.getException(), "");
            ExceptionInnerLogger.writeLog(e, "");
        }
        Exception exception = exceptionContext.getException();
        //查找CAFRuntimeException
        //获取内部异常
        Throwable innerException = exception;
        while (innerException != null) {
            if (innerException instanceof CAFRuntimeException)
                break;
            if (innerException instanceof InvocationTargetException)
                innerException = ((InvocationTargetException) innerException).getTargetException();
            else
                innerException = innerException.getCause();
        }
        //异常处理
        if (innerException != null)
            return this.wrapCAFRuntimeException((CAFRuntimeException) innerException);
        return exception;
    }

    /**
     * 对 CAFRuntimeException的处理逻辑
     *
     * @param serverException 待处理的异常
     * @return 处理之后的异常类
     */
    private CAFRuntimeException wrapCAFRuntimeException(CAFRuntimeException serverException) {
        try {
            List<Object> listResult = wrapExceptionMessage(serverException);
            boolean isBizException = Boolean.parseBoolean(listResult.get(0).toString());
            String expMessage = listResult.get(2).toString();
            String expCode = listResult.get(1).toString();
            ExceptionLevel expLevel = (ExceptionLevel) listResult.get(3);
            HashMap extensionMessage = (HashMap) listResult.get(4);

            return new CAFRuntimeServerException(expCode, expMessage, serverException, expLevel, isBizException, extensionMessage);
        } catch (Exception e) {
            ExceptionInnerLogger.writeLog(e, "==============WrapCAFRuntimeException==========");
            return new CafExceptionHandleException("caf", ExceptionErrorCode.exceptionHandleError, serverException.getMessage(), serverException);
        }
    }

    /**
     * 查找内部堆栈中最顶层的Info或者Warning级别的异常
     *
     * @param exp 传入的异常堆栈
     * @return Info或者Warning的异常
     */
    private CAFRuntimeException findInfoOrWarningException(CAFRuntimeException exp) {
        CAFRuntimeException returnException = null;
        if (exp.getLevel() == ExceptionLevel.Info || exp.getLevel() == ExceptionLevel.Warning)
            returnException = exp;
        var innerException = exp.getCause();
        while (innerException != null) {
            var innerExp = innerException instanceof CAFRuntimeException ? (CAFRuntimeException) innerException : null;
            if (innerExp == null)
                return returnException;
            if (innerExp.getLevel() == ExceptionLevel.Info || innerExp.getLevel() == ExceptionLevel.Warning)
                returnException = innerExp;

            if (innerException instanceof InvocationTargetException)
                innerException = ((InvocationTargetException) innerException).getTargetException();
            else
                innerException = innerException.getCause();
        }
        return returnException;
    }


    /**
     * 异常消息的封装
     *
     * @param cafRuntimeException 异常
     * @return 列表包含是否业务异常、异常编号、异常消息、异常级别
     */
    private List<Object> wrapExceptionMessage(CAFRuntimeException cafRuntimeException) {
        try {
            boolean isBizEx = false;
            String exceptionCode = cafRuntimeException.getExceptionCode();
            String exceptionMessage = cafRuntimeException.getMessage();
            ExceptionLevel exceptionLevel = cafRuntimeException.getLevel();

            CAFRuntimeException topBizException = null;
            CAFRuntimeException bottomBizException = null;
            boolean isTopBizExceptionFound = false;

            //判断最外层异常是否为业务异常
            if (cafRuntimeException.isBizException()) {
                topBizException = cafRuntimeException;
                isTopBizExceptionFound = true;
                isBizEx = true;
            }

            //获取内部异常
            var innerException = cafRuntimeException.getCause();
            var extensionMessage = cafRuntimeException.getExtensionMessage();
            //循环获取 topBizException  bottomBizException
            while (innerException != null) {
                var exp = innerException instanceof CAFRuntimeException ? (CAFRuntimeException) innerException : null;
                if (exp == null) {
                    if (innerException instanceof InvocationTargetException)
                        innerException = ((InvocationTargetException) innerException).getTargetException();
                    else
                        innerException = innerException.getCause();
                    continue;
                }
                extensionMessage = ((CAFRuntimeException) innerException).getExtensionMessage();
                if (!exp.isBizException()) {
                    innerException = innerException.getCause();
                    continue;
                }
                isBizEx = true;
                if (!isTopBizExceptionFound) {
                    topBizException = exp;
                    isTopBizExceptionFound = true;
                } else {
                    bottomBizException = exp;
                }
                innerException = innerException.getCause();
            }

            //获取异常消息
            StringBuilder sb = new StringBuilder(400);
            if (topBizException != null)
                sb.append(topBizException.getMessage());
            if (bottomBizException != null) {
                sb.append("(");
                sb.append(bottomBizException.getMessage());
                sb.append(")");
                exceptionCode = bottomBizException.getExceptionCode();
                exceptionLevel = bottomBizException.getLevel();
            } else if (topBizException != null) {
                exceptionCode = topBizException.getExceptionCode();
                exceptionLevel = topBizException.getLevel();
            }
            String str = sb.toString();
            if (!"".equals(str)) {
                exceptionMessage = str;
            }

            //返回列表
            List<Object> list = new ArrayList<>();
            list.add(isBizEx);
            list.add(exceptionCode);
            list.add(exceptionMessage);
            list.add(exceptionLevel);
            list.add(extensionMessage);
            return list;
        } catch (Exception e) {
            ExceptionInnerLogger.writeLog(e, "==============异常信息包装过程中发生了异常==========");
        }
        return null;
    }
}
